'''
Created on Apr 28, 2011

@author: jlucasgl
'''

# from yacf.MiddleEnd.Loop.Analysis import ParametrizeLoopTool
from yacf.Backends.Common.Mutators.AbstractMutator import AbstractMutator

from yacf.Tools.Tree import ReplaceTool, getContainerAttribute
from yacf.Frontend.Shortcuts import getCurrentLanguageAst
from yacf.Backends.Common.Visitors.GenericVisitors import GenericFilterVisitor
c_ast = getCurrentLanguageAst()

from yacf.Frontend.Omp import omp_ast

class LoopUnswitchFilter(GenericFilterVisitor):
    """ Returns the first node with the given attribute
    """
    def __init__(self):
        def condition(node):
            if type(node) == omp_ast.llcUnswitch:
                return True
            return False
        super(LoopUnswitchFilter, self).__init__(condition_func = condition)

from yacf.Tools.Tree import NodeNotFound

class IfWithinLoopUnswitchFilter(GenericFilterVisitor):
    """ Returns the first node with the given attribute
    """
    def __init__(self):
        def condition(node):
            if type(node) == c_ast.If:
                return True
            return False
        super(IfWithinLoopUnswitchFilter, self).__init__(condition_func = condition)

    def get_if_node_and_common_block_items(self, for_ast):
        """ Return if node and rest of block_items within a loop if node if exists in for_ast
            :param for_ast: The node where the function will apply the filter
        """
        try:
            if_ast = IfWithinLoopUnswitchFilter().apply(for_ast)
                     
            loop_block_items = for_ast.stmt.block_items
            common_block_items = []
            for node in loop_block_items:
                #node.show()
                if type(node) != c_ast.If:
                    common_block_items.append(node)
            #c_ast.Compound(block_items = common_block_items).show()
            return [if_ast, common_block_items]
        except NodeNotFound:
            return [None, []]


class UnswitchLoopMutator(AbstractMutator):
    def __init__(self):
        pass
 
    def filter(self, ast):
        """  """
        raise NotImplemented

    def filter_iterator(self, ast):
        """ Fast filter  """
        return NotImplemented

    def fast_filter(self, ast):
        """ Fast filter , looking for binary expressions """
        return LoopUnswitchFilter().dfs_iter(ast)
    
    def mutatorFunction(self, loopUnswitch_directive_ast):
        
        for_ast = loopUnswitch_directive_ast.loop
        
        assert isinstance(for_ast, c_ast.For)

        # Parametrize
#        parametrizer = ParametrizeLoopTool()
#        loop_parameters = parametrizer.apply(for_ast,)
#        print "loop_parameters", loop_parameters

        import copy
        # Note the deepcopy operation instead a copy or a bare assignment
#        old_parameters = copy.deepcopy(loop_parameters)
 
        # 1. Localize If node and common block items inside loop for
        unswitch_parent = loopUnswitch_directive_ast.parent
        unswitch_node = loopUnswitch_directive_ast
        loop_block_items = for_ast.stmt.block_items

#        for_ast.show()

        [if_ast, common_block_items] = IfWithinLoopUnswitchFilter().get_if_node_and_common_block_items(for_ast)
        assert isinstance(if_ast, c_ast.If)

        unswitch_parent_parent = loopUnswitch_directive_ast.parent.parent
        #from yacf.Tools.Debug import DotDebugTool
        #DotDebugTool(highlight=[unswitch_node,if_ast]).apply(unswitch_parent_parent)

        
        # 2. Build list of block items for the two loops for
        import copy 
        block_items_iftrue = common_block_items
        block_items_iffalse = copy.deepcopy(common_block_items)

        if if_ast.iftrue.block_items: block_items_iftrue += if_ast.iftrue.block_items
        if if_ast.iffalse: block_items_iffalse += if_ast.iffalse.block_items

        # 3. Generate the if statement
        # TODO: parametrizer
        for_iftrue = c_ast.For(init = for_ast.init,
                                    cond = for_ast.cond,
                                    next = for_ast.next,
                                    stmt = c_ast.Compound(block_items = block_items_iftrue)
                                   )

        for_iffalse = c_ast.For(init = for_ast.init, 
                                    cond = for_ast.cond, 
                                    next = for_ast.next, 
                                    stmt = c_ast.Compound(block_items = block_items_iffalse)
                                   )

        final_if = c_ast.If (cond = if_ast.cond,
                                    iftrue = for_iftrue,
                                    iffalse = for_iffalse
                                   )

        # setattr(unswitch_parent.parent, getContainerAttribute(unswitch_parent), final_if)

        # Replace the forLoop with the unswitched compound statement
        ReplaceTool(new_node = final_if, 
                    old_node = unswitch_parent
                    ).apply(unswitch_parent.parent, 
                            'block_items'
                            )

        #DotDebugTool(highlight=[for_iftrue,for_iffalse]).apply(unswitch_parent_parent)


